home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Add-Ons / MPW / MPW rman 1.3.4 / rman.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-08  |  60.2 KB  |  2,086 lines  |  [TEXT/KAHL]

  1. /*
  2.   RosettaMan
  3.  
  4.   Copyright (c) 1993-1995  T.A. Phelps (phelps@cs.Berkeley.EDU)
  5.   All Rights Reserved.
  6.  
  7.      Permission to use, copy, modify, and distribute this software and its
  8.      documentation for educational, research and non-profit purposes, 
  9.      without fee, and without a written agreement is hereby granted, 
  10.      provided that the above copyright notice and the following 
  11.      paragraph appears in all copies.  
  12.  
  13.      Permission to incorporate this software into commercial products may 
  14.      be obtained from the Office of Technology Licensing, 2150 Shattuck 
  15.      Avenue, Suite 510, Berkeley, CA  94704.
  16.  
  17.   $Header: /home/auspex/h/bair/phelps/spine/rman/RCS/rman.c,v 1.34 1995/07/02 01:01:49 phelps Exp phelps $
  18. */
  19.  
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <stdlib.h>
  24. /* OSF seems to need this */
  25. #ifdef I_UNISTD
  26. #include <unistd.h>
  27. #endif /* I_UNISTD */
  28.  
  29. /*** make #define's into consts? => can't because compilers not smart enough ***/
  30. /* maximum number of tags per line */
  31. #define MAXTAGS 50
  32. #define MAXTOC 500
  33. /* minimum column for right margin */
  34. #define MINRM 50
  35. #define MINMID 20
  36. /*#define MAXINDENT 15*/
  37. /*#define HEADFOOTMATCH 20*/
  38. #define HEADFOOTSKIP 20
  39. #define HEADFOOTMAX 25
  40. /* length of unique filter prefix */
  41. #define UFP 2
  42. #define xputchar(c)        if (fcharout) putchar(c)
  43. /*enum { c_dagger=0xa7, c_plusminus=0xb1, c_bullet=0xb7 };*/
  44. #define c_bullet '\xb7'
  45. #define c_plusminus '\xb1'
  46. #define c_dagger '\xa7'
  47. #define c_lsquote '\x60'
  48. #define c_rsquote '\x27'
  49.  
  50.  
  51.  
  52. /*
  53.   accept man pages as formatted by (10)
  54.      Hewlett-Packard HP-UX, AT&T System V, SunOS, Sun Solaris, OSF/1, DEC Ultrix,
  55.     SGI IRIX, Linux, FreeBSD
  56.  
  57.   output as (10)
  58.      printable ASCII, headers only, TkMan, [tn]roff, Ensemble, SGML, HTML, LaTeX, RTF, Perl pod
  59.  
  60.      written March 24, 1993
  61.     bs2tk transformed into RosettaMan November 4-5, 1993
  62.  
  63.    1993
  64.     2-Apr  bullets, change bars, copyright symbol
  65.     5      boldface, other SGI nicks
  66.     7      skip unrecognized escape codes
  67.    10      small caps
  68.    13      underscores considered uppercase so show up
  69.               in default small caps font
  70.            screen out Ultrix junk (code getting pretty tangled now)
  71.    14      until Tk text has better tab support, replace tabs by
  72.            spaces until get to next tab stop (for Ultrix)
  73.            -t gives tabstop spacing
  74.    20      Solaris support (Larry Tsui)
  75.     3-Jun  section subheading parsing (Per-Erik Martin)
  76.    28      hyphenated man pages in SEE ALSO show up correctly in Links
  77.            (Mike Steele)
  78.    13-Jul  under FILES, fully qualified path names are added to Links,
  79.               but this taken out immediately because not useful
  80.    14      option to keep changebars on right (Warren Jessop)
  81.     5-Aug  search for header, footer dynamically--
  82.               no need to edit or search large list of patterns
  83.    11      -m kicks in man page formatting beyond nroff backspace kludges
  84.    27      handle double digit numbers better by trying again relative to end of line
  85.    19-Sep  -T gives Tk extras (otherwise ASCII only)
  86.            -H gives headers only (implies -T off)
  87.    10-Oct  -r reverse compiles to [tn]roff source (as Geoff Collyer's nam and fontch,
  88.            but leveraging existing analysis so only addition of ~60 lines)
  89.            (The code is device-driver obscure now--obfuscated C contest next.)
  90.    13      header and footer optionally available at bottom in Tk view
  91.            (Marty Leisner)
  92.    19      "reflected" odd and even page headers&footers zapped
  93.    20      keep count of sections and subsections, using smaller font for larger numbers
  94.     1-Nov  reverse compiles to Ensemble, except for character ranges
  95.  
  96.     4      started rman rewrite for cleaner support of multiple output targets,
  97.            including: plain ascii, headers only, TkMan, [nt]roff, Ensemble, SGML, HTML
  98.     5      line filtering separated from other logic
  99.            despite greater sophistication, RosettaMan faster than bs2tk (!)
  100.    28-Dec  man page reference recognition (Michael Harrison)
  101.  
  102.    1994
  103.     1-Jan  identify descriptive lists by comparing scnt2 with s_avg
  104.     3      tail-end table of contents in HTML documents
  105.     5      -f <filter> and LaTeX output mode
  106.    24      proof-of-concept RTF output mode
  107.    26      handle man pages that don't have a header on the first page
  108.    28      parse "handwritten" man pages
  109.    22-Feb  alpha version released
  110.     6-Mar  various bug fixes
  111.    10      beta version released
  112.    13-Jun  fixed surious generation on <DL>'s (the existence of which pointed out by David Sibley)
  113.    22-Jul  table recognition experiment.
  114.               works reasonably well, except for tables with centered headers
  115.     3      allow for off-by-one (and -two) in identification of header and footer
  116.            fixed problem with recurrent/leftover text with OSF/1 bold bullets (yeesh)
  117.    12-Sep  2.0gamma released
  118.    13      check for *third* header, possibly centered, possibly after blank lines (Charles Anderson)
  119.            fixed tag ranges for lines following blank lines (just \n)
  120.               of pages with global indentation (Owen Rees)
  121.    19      fixed two small problems with LaTeX (^ => \^, \bullet => $\bullet$) (Neal Becker)
  122.    24      simple check for erroneously being fed roff source
  123.    26      deal with bold +- as in ksh (ugh)
  124.    30      2.0delta released
  125.     9-Oct  special check for OSF to guard against section head interpreted as footer
  126.     8-Nov  Perl pod output format (result still needs work, but not much)
  127.     7-Dec  2.0epsilon released (last one before final 2.0)
  128.    22      Happy Winter Solstice!  2.0 released
  129.            deprecated gets() replaced (Robert Withrow)
  130.    25      TkMan module's $w.show => $t, saving about 9% in generated characters
  131.  
  132.    1995
  133.     1-Jan  experiment with TkMan output to take advantage of my hack to Tk text
  134.            (i.e., $t insert end "text" => $t insert end "text1" tag1 "text2" tag2 ...)
  135.          results => output size reduced about 25%, time reduced about 12-15%
  136.    25-Mar  back to old mark command for Tk module
  137.     8-May  hyphens in SEE ALSO section would confuse link-finder, so re-linebreak if necessary(!)
  138.            (Greg Earle & Uri Guttman)
  139.  
  140.    ??-???  SGML output format (DTD found at long last), validated by sgmls
  141. */
  142.  
  143.  
  144. /* TO DO ****
  145.  
  146.    output to SGML with Davenport DTD
  147.  
  148.    fix spurious additional newlines, as after umount Flags in /usr/man/man8/mount.8
  149.    don't give SHORTLINE if just finished bullet of bultxt, ended section head, ... other cases?
  150.    make sure text following bullet is aligned correctly
  151.  
  152.    output to MIME?
  153.    output to WinHelp?
  154.    collect header and footer until hit blank line?
  155.    what to do about tables?   count second gap of spaces & average gap? ==>
  156.       good idea but tables too variable for this to work
  157.    internal, outline-like header section for HTML documents?  how to put this *first*?
  158.    one line look ahead to enable better parsing (item lists, et cetera)
  159.    alluc (==nonlc) flag, copy curline to last line vector (works well with lookahead cache)
  160.    ??      collect sundry globals into vectors (i.e., arrays and enum indexes)
  161.               (if compiler has good constant propagation, then shouldn't slow access)
  162.    collect scattered globals into vectors (e.g., curline[ispcnt]): array + enum
  163.       curline, lastline, flags, pending, bufs+lens
  164. */
  165.  
  166.  
  167.  
  168. /*** tag management ***/
  169.  
  170. enum tagtype { TITLE, ITALICS, BOLD, SYMBOL, SMALLCAPS, BOLDITALICS, MANREF, MONO };
  171.  
  172. struct { int type; int first; int last; } tags[MAXTAGS+1];
  173. int tagc=0;
  174. struct { char *text; int type; } toc[MAXTOC];
  175. int tocc=0;
  176.  
  177.  
  178. /*** globals ***/
  179. /* move all flags into an array?
  180. enum { fSubsX, fLast };
  181. int flags[fLast];
  182. */
  183.  
  184. int TabStops=8;
  185. int fSubsections=0;    /* extract subsection titles too? */
  186. int fChangeleft=0;    /* move change bars to left */
  187. int fChangezap=0;    /* delete change bars */
  188. int fMan=1;        /* invoke agressive man page filtering? */
  189. int fQS=0;        /* squeeze out spaces (scnt and interword)? */
  190. int fIQS=0;        /* squeeze out initial spaces (controlled separately from fQS) */
  191. int fILQS=0;        /* squeeze out spaces for usual indent */
  192. int fHeadfoot=0;    /* show canonical header and footer at bottom? */
  193. int falluc=0;
  194. int fintable=0;
  195. int fTable=0;
  196. int fotable=0;
  197. int hanging=0;        /* location of hanging indent (if ==0, none) */
  198. int fSEEALSO=0;    /* in SEE ALSO section? */
  199. int fFILES=0;
  200. int fNOHY=0;        /* re-linebreak so no words are hyphenated */
  201. char manName[80]="man page";
  202. char manSect[80]="1";
  203. char *manTitle = MANTITLEPRINTF;
  204. char *manRef = MANREFPRINTF;
  205. char *providence =
  206.     "manual page source format generated by RosettaMan";
  207. char *anonftp =
  208.     "available via anonymous ftp from ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z";
  209.  
  210. int pmode=0;            /* line or paragraph groupings of text */
  211. int linelen;            /* length of result in plain[] */
  212. int spcsqz;            /* number of spaces squeezed out */
  213. int ccnt=0;            /* # of changebars */
  214. int scnt,scnt2;        /* counts of initial spaces in line */
  215. int s_sum,s_cnt;
  216. int bs_sum, bs_cnt;
  217. int ncnt=0,oncnt=0;        /* count of interline newlines */
  218. int CurLine=1;
  219. int indent=0;            /* global indentation */
  220. int lindent=0;            /* usual local indent */
  221. int auxindent=0;        /* aux indent */
  222. int I;                /* index into line/paragraph */
  223. int fcharout=1;        /* show text or not */
  224. char *escchars="";
  225. char lookahead='\0';
  226. char buf[BUFSIZ];
  227. char plain[BUFSIZ];        /* current text line with control characters stripped out */
  228. char hitxt[BUFSIZ];        /* highlighted text (available at BEGIN<highlight> signal */
  229. char header[BUFSIZ]="";        /* complete line */
  230. char footer[BUFSIZ]="";
  231. char header2[BUFSIZ]="";        /* SGIs have two lines of headers and footers */
  232. char header3[BUFSIZ]="";        /* GNU and some others have a third! */
  233. char footer2[BUFSIZ]="";
  234.  
  235. int Psect=0, Psub=0, Pbp=0, Pbt=0, Pb=0;
  236. int fIP=0;
  237.  
  238.  
  239.  
  240. /*** utility functions ***/
  241.  
  242. void
  243. addtag(int type, int first, int last) {
  244.     if (tagc<MAXTAGS) {
  245.         tags[tagc].type = type;
  246.         tags[tagc].first = first;
  247.         tags[tagc].last = last;
  248.         tagc++;
  249.     }
  250. }
  251.  
  252.  
  253. /*
  254.    collect all saves to string table one one place, so that
  255.    if decide to go with string table instead of multiple malloc, it's easy
  256.    (probably few enough malloc's that more sophistication is unnecessary)
  257. */
  258.  
  259. void
  260. addtoc(char *text, int type, int endline) {
  261.     char *r;
  262.  
  263.     if (tocc<MAXTOC) {
  264.         r = malloc(strlen(text)+1);
  265.         strcpy(r,text);
  266.         toc[tocc].text = r;
  267.         toc[tocc].type = type;
  268.         tocc++;
  269.     }
  270. }
  271.  
  272.  
  273. char phrase[BUFSIZ];    /* first "phrase" (space of >=3 spaces) */
  274. int phraselen;
  275.  
  276. void
  277. filterline(char *buf, char *plain) {
  278.     char *p,*q,*r;
  279.     char pp='\0';
  280.     char *ph;
  281.     int ip,iq;
  282.     int i;
  283.     int hl=-1, hl2=-1;
  284.     int iscnt=0;    /* interword space count */
  285.     enum tagtype tag;
  286.  
  287.     ph=phrase; phraselen=0;
  288.     scnt=scnt2=0;
  289.     s_sum=s_cnt=0;
  290.     bs_sum=bs_cnt=0;
  291.     ccnt=0;
  292.     spcsqz=0;
  293.  
  294.     /* strip only certain \x1b's and only at very beginning of line */
  295.     for (p=buf; *p=='\x1b'&& (p[1]=='8'||p[1]=='9'); p+=2)
  296.         /* nop */;
  297.  
  298.     /*** tabs => spaces ***/
  299.     for (iq=0, /* p=buf,-- p set above!*/ q=plain; *p; p++) {
  300.         if (*p=='\t') {
  301.             do { *q++=' '; iq++; } while (iq%TabStops);
  302.         } else {
  303.             *q++=*p; iq++;
  304.             if (*p=='\b') iq-=2;
  305.         }
  306.     }
  307.     *q='\0';
  308.  
  309.     /*** spaces and change bars ***/
  310.     for (scnt=0,p=plain; *p==' '; p++) scnt++;    /* initial space count */
  311.     if (scnt) pp=' ';
  312.  
  313.     q--;
  314.     if (fChangeleft || fChangezap)
  315.         for (; q-40>plain && *q=='|'; q--)        /* change bars */
  316.             if (fChangeleft) ccnt++;
  317.  
  318.     if (q!=&plain[scnt-1])            /* trailing */
  319.         for (; *q==' '; q--) /* nop */;
  320.  
  321.     q[1]='\0';
  322.  
  323.  
  324.     /* set I for tags below */
  325.     if (indent>=0 && scnt>=indent) scnt-=indent;
  326.     if (!pmode && !fIQS) {
  327.         if (fChangeleft) I+=(scnt>ccnt)?scnt:ccnt;
  328.         else I+=scnt;
  329.     }
  330.  
  331.  
  332.     /*** tags and filler spaces ***/
  333.  
  334.     iq=0; falluc=1;
  335.     for (q=plain; *p; pp=*p,p++) {
  336.  
  337.         iscnt=0;
  338.         if (*p==' ') {
  339.             for (r=p; *r==' '; r++) { iscnt++; spcsqz++; }
  340.             s_sum+=iscnt; s_cnt++;
  341.             if (iscnt>1 && !scnt2 && *p==' ') scnt2=iscnt;
  342.             if (iscnt>2) { bs_cnt++; bs_sum+=iscnt; }    /* keep track of large gaps */
  343.             iscnt--;        /* leave last space for tail portion of loop */
  344.  
  345.             if (fQS && iscnt<3) { p=r-1;    iscnt=0; } /* reduce strings of <3 spaces to 1 */
  346.             /* else if (fQS && iscnt>=3) { replace with tab? } */
  347.             else {
  348.                 for (i=0; i<iscnt; i++) { p++; *q++=' '; }
  349.             }
  350.             pp=' ';
  351.         } /* need to go through if chain for closing off annotations */
  352.  
  353.         /** backspace-related filtering **/
  354.  
  355.         /* else */ if (*p=='\b' && p[1]=='_' && q>plain && q[-1]=='+') {
  356.             /* bold plus/minus(!) */
  357.             q[-1]=c_plusminus;
  358.             while (*p=='\b' && p[1]=='_') p+=2;
  359.             continue;
  360.         } else if ((*p=='_' && p[1]=='\b' && p[2]!='_' && p[3]!='\b')
  361.             || (*p=='\b' && p[1]=='_')) {
  362.             /* italics */
  363.             /* start tag only if not already in one */
  364.             if (hl==-1) { hl=I+iq; tag=ITALICS; }
  365.             p+=2;
  366.         } else if (*p=='_' && p[2]==p[4] && p[1]=='\b' && p[3]=='\b' && p[2]!='_') {
  367.             /* bold italics (Solaris is BRAIN DEAD!) */
  368.             for (p+=2; *p==p[2] && p[1]=='\b';)
  369.                 p+=2;
  370.             if (hl==-1) { hl=I+iq; tag=BOLDITALICS; }
  371.         } else if (*p==p[2] && p[1]=='\b') {
  372.             /* boldface */
  373.             while (*p==p[2] && p[1]=='\b')
  374.                 p+=2;
  375.             if (hl==-1) { hl=I+iq; tag=BOLD; }
  376.         } else if (p[1]=='\b' &&
  377.                  ((*p=='o' && p[2]=='+') ||
  378.                  (*p=='+' && p[2]=='o')) ) {
  379.             /* bullets */
  380.             p+=2;
  381.             while (p[1]=='\b' &&        /* bold bullets(!) */
  382.                  (*p=='o' || p[2]=='+') )
  383.                 p+=2;
  384.             *q++=c_bullet; iq++;
  385.             continue;
  386.         } else if (*p=='\b' && p>plain && p[-1]=='o' && p[1]=='+') {
  387.             /* OSF bullets */
  388.             while (*p=='\b' && p[1]=='+') p+=2;    /* bold bullets(!) */
  389.             q[-1]=c_bullet; p--;
  390.             continue;
  391.         } else if (p[1]=='\b' && *p=='+' && p[2]=='_') {
  392.             /* plus/minus */
  393.             p+=2;
  394.             *q++=c_plusminus; iq++;
  395.             continue;
  396.         } else if (p[1]=='\b' && *p=='|' && p[2]=='-') {
  397.             /* dagger */
  398.             *q++=c_dagger; iq++;
  399.             p+=2; continue;
  400.         } else if (*p=='\b') {
  401.             /* supress unattended backspaces */
  402.             continue;
  403.         } else if (*p=='\x1b' /*&& (p[1]=='9'||p[1]=='8')*/) {
  404.             p++;
  405.             if (*p=='[') {
  406.                 p++;
  407.                 if (*p=='1' && hl==-1) { tags[MAXTAGS].first=I+iq; tags[MAXTAGS].type=BOLD; }
  408.                 else if (*p=='0' && hl2==-1 && tags[MAXTAGS].first<I+iq) {
  409.                     /* doesn't catch tag if spans line */
  410.                     addtag(tags[MAXTAGS].type, tags[MAXTAGS].first, I+iq);
  411.                 }
  412.                 p++;
  413.                 /* following 'm' (why?) gobbled in overarching for */
  414.             }
  415.             /* skip unrecognized escape codes */
  416.             continue;
  417.         } else if ((isupper(*p) /*|| *p=='_'*/ || *p=='&') &&
  418.                  (hl>=0 || isupper(p[1]) || p[1]=='&')) {
  419.             if (hl==-1) { hl=I+iq; tag=SMALLCAPS; }
  420.         } else {
  421.             /* end of tag, one way or another */
  422.             /* collect tags in this pass, interspersed later if need be */
  423.             /* can't handle overlapping tags */
  424.             if (hl>=0) {
  425.                 if (hl2==-1) addtag(tag, hl, I+iq);
  426.                 hl=-1;
  427.             }
  428.         }
  429.  
  430.         /** non-backspace related filtering **/
  431.         /* case statement here in place of if chain? */
  432. /* Tk 3.x's text widget tabs too crazy
  433.         if (*p==' ' && strncmp("     ",p,5)==0) {
  434.             xputchar('\t'); i+=5-1; ci++; continue;
  435.         } else
  436. */
  437. /* copyright symbol: too much work for so little
  438.         if (p[i]=='o' && (strncmp("opyright (C) 19",&p[i],15)==0
  439.                     || strncmp("opyright (c) 19",&p[i],15)==0)) {
  440.             printf("opyright \xd3 19");
  441.             addtag(SYMBOL, ci+9, ci+10);
  442.             i+=15-1; ci+=13; continue;
  443.         } else
  444. */
  445.         if (*p=='(' && q>plain && (isalnum(q[-1])||strchr("._-+",q[-1])!=NULL)
  446.             && strchr("123456789olnp",p[1])!=NULL 
  447.             /* && p[1]!='s' && p[-1]!='`' && p[-1]!='\'' && p[-1]!='"'*/ ) {
  448.             hl2=I+iq;
  449.             for (r=q-1; r>=plain && (isalnum(*r)||strchr("._-+",*r)!=NULL); r--)
  450.                 hl2--;
  451.             /* else ref to a function? */
  452.             /* maybe save position of opening paren so don't highlight it later */
  453.         } else if (*p==')' && hl2!=-1) {
  454.             /* don't overlap tags on man page referenes */
  455.             while (tagc>0 && tags[tagc-1].last>hl2) tagc--;
  456.             addtag(MANREF, hl2, I+iq+1);
  457.             hl2=-1;
  458.         } else if (hl2!=-1) {
  459.             if (!isalnum(*p)) hl2=-1;
  460.         }
  461.  
  462.  
  463.         if (!*p) break;    /* safety check */
  464.         *q++=*p;
  465.         falluc = falluc && (isupper(*p) || isspace(*p) || *p=='-' || *p=='&' || *p=='_');
  466.         if (!scnt2) { *ph++=*p; phraselen++; }
  467.         iq+=iscnt+1;
  468.     }
  469.     if (hl>=0) addtag(tag, hl, I+iq);
  470.     *q=*ph='\0';
  471.     linelen=iq+ccnt;
  472. }
  473.  
  474.  
  475. void
  476. lowerline(char *p, char *q) {
  477.     while (*p) *q++=tolower(*p++);
  478.     *q='\0';
  479. }
  480.  
  481.  
  482.  
  483.  
  484. /*
  485.  * OUTPUT FORMATS
  486.  *    *** break these out so can selectively include them in the binary ***
  487.  *    *** does this save significant space? ***
  488.  */
  489.  
  490. enum command {
  491.     BEGINDOC, ENDDOC, BEGINBODY, ENDBODY,
  492.     BEGINHEADER, ENDHEADER, BEGINFOOTER, ENDFOOTER, SHORTLINE,
  493.     BEGINSECTION, ENDSECTION, BEGINSUBSECTION, ENDSUBSECTION,
  494.     BEGINSECTHEAD, ENDSECTHEAD, BEGINSUBSECTHEAD, ENDSUBSECTHEAD,
  495.     BEGINBOLD, ENDBOLD, BEGINITALICS, ENDITALICS, BEGINMANREF, ENDMANREF,
  496.     BEGINSC, ENDSC, BEGINBOLDITALICS, ENDBOLDITALICS, BEGINY, ENDY,
  497.     BEGINBULPAIR, ENDBULPAIR, BEGINBULLET, ENDBULLET, BEGINBULTXT, ENDBULTXT,
  498.     CHARLQUOTE, CHARRQUOTE, CHARPERIOD, CHARDAGGER, CHARBULLET, CHARPLUSMINUS,
  499.     CHARLSQUOTE, CHARRSQUOTE, CHARGT, CHARLT, CHARAMP, CHANGEBAR, CHARBACKSLASH, CHARDASH,
  500.     BEGINLINE, ENDLINE, BEGINTABLELINE, ENDTABLELINE, BEGINTABLE, ENDTABLE
  501. };
  502. void (*fn)(enum command);
  503. enum command prevcmd = BEGINDOC;
  504.  
  505.  
  506.  
  507. /*
  508.  * TkMan
  509.  */
  510.  
  511. void
  512. manStrip(char *s) {
  513.     if (*s) {
  514.         printf("$t insert end {%s} sc \\n\n",s);
  515.         CurLine++;
  516.     }
  517. }
  518.  
  519. void
  520. TkMan(enum command cmd) {
  521.     static char *headfoot = "Header and Footer";
  522.     static int skip=0;
  523.     static int markcnt=0;
  524.     static char *bads = "\"[]$";
  525.     int i;
  526.  
  527.  
  528.     /* invariant: always ready to insert text */
  529.  
  530.     switch (cmd) {
  531.        case BEGINDOC:
  532.         I=0; CurLine=1;
  533.         escchars = bads;
  534.         printf("$t insert end \"");
  535.         break;
  536.        case ENDDOC:
  537.         if (fHeadfoot) {
  538. /*    grr, should have +mark syntax for Tk text widget!
  539.         printf("\\n\\n\" {} \"%s\\n\" {+headfoot h2}\n",headfoot);
  540. */
  541.             printf("\\n\\n\" {} \"%s\\n\" h2\n",headfoot);
  542.             printf("$t mark set headfoot %d.0\n");
  543.             CurLine++;
  544.  
  545.             manStrip(header); manStrip(header2); manStrip(header3);
  546.             manStrip(footer); manStrip(footer2);
  547.         } else printf("\"\n");
  548.         break;
  549.  
  550.        case BEGINLINE:
  551.         /*I=0; -- need to do this at end of line so set for filterline() */
  552.         /* nothing to do at start of line except catch up on newlines */
  553.         for (i=0; i<ncnt; i++) printf("\\n");
  554.         CurLine+=ncnt;
  555. /*        if ((CurLine&0x1f)==0x1f) printf("\" {}\n$t insert end \"");*/
  556.         break;
  557.        case ENDLINE:
  558.         if (!skip) {
  559.             printf("\\n");
  560.             if (fSEEALSO) {
  561.                 printf("\"\n");
  562.                 printf("append manx(links) {%s,}\n", plain);
  563.                 printf("$t insert end \"");
  564.             }
  565.         }
  566.  
  567.         tagc=0;
  568.         skip=0;
  569.         CurLine++; I=0;
  570.         if ((CurLine&0x3f)==0x3f) printf("\" {}\nupdate idletasks\n$t insert end \"");
  571.         break;
  572.  
  573.        case ENDSECTHEAD:
  574. /*        printf("\\n\" {h2 +js%d}\n$t insert end \"",++markcnt); skip=1;*/
  575.         printf("\\n\" h2\n");
  576.         printf("$t mark set js%d %d.0\n", ++markcnt, CurLine);
  577.         tagc=0;
  578.         printf("$t insert end \""); skip=1;
  579.         break;
  580.        case ENDSUBSECTHEAD:
  581. /*        printf("\\n\" {+jss%d}\n$t insert end \"",++markcnt); skip=1;*/
  582.         printf("\\n\"\n");    /* add h3? */
  583.         printf("$t mark set jss%d %d.0\n", ++markcnt, CurLine);
  584.         tagc=0;
  585.         printf("$t insert end \""); skip=1;
  586.         break;
  587.        case BEGINTABLELINE:
  588.         break;
  589.        case ENDTABLELINE:
  590.         printf("\" tt \"");
  591. /*        addtag(MONO, 0, I);*/
  592.         break;
  593.  
  594.        case CHARLQUOTE:
  595.        case CHARRQUOTE:
  596.         putchar('\\'); putchar('"'); I++;
  597.         break;
  598.        case CHARLSQUOTE:    putchar('`'); I++; break;
  599.        case CHARRSQUOTE:    putchar('\''); I++; break;
  600.        case CHARPERIOD:        putchar('.'); I++; break;
  601.        case CHARDASH:        putchar('-'); I++; break;
  602.        case CHARLT:        putchar('<'); I++; break;
  603.        case CHARGT:        putchar('>'); I++; break;
  604.        case CHARAMP:        putchar('&'); I++; break;
  605.        case CHARBACKSLASH:    printf("\\\\"); I++; break;
  606.        case CHARDAGGER:        putchar(c_dagger); I++; break;
  607.        case CHARBULLET:        printf("\" {} %c symbol \"",c_bullet); I++; break;
  608.        case CHARPLUSMINUS:    putchar(c_plusminus); I++; break;
  609.  
  610.  
  611.        case BEGINSECTHEAD:
  612.        case BEGINSUBSECTHEAD:
  613.         tagc=0;    /* section and subsection formatting controlled descriptively */
  614.         /* no break;*/
  615.  
  616.        case BEGINBOLD:
  617.        case BEGINITALICS:
  618.        case BEGINBOLDITALICS:
  619.        case BEGINY:
  620.        case BEGINSC:
  621.        case BEGINMANREF:
  622.         /* end text, begin attributed text */
  623.         printf("\" {} \"");
  624.         break;
  625.  
  626.        /* rely on the fact that no more than one tag per range of text */
  627.        case ENDBOLD:        printf("\" b \""); break;
  628.        case ENDITALICS:        printf("\" i \""); break;
  629.        case ENDBOLDITALICS:    printf("\" bi \""); break;
  630.        case ENDY:            printf("\" symbol \""); break;
  631.        case ENDSC:            printf("\" sc \""); break;
  632.        case ENDMANREF:        printf("\" manref \""); break;
  633.         /* presentation attributes dealt with at end of line */
  634.  
  635.        case BEGINBODY: case ENDBODY:
  636.        case SHORTLINE:
  637.        case BEGINBULPAIR: case ENDBULPAIR:
  638.        case BEGINBULLET: case ENDBULLET:
  639.        case BEGINBULTXT: case ENDBULTXT:
  640.        case BEGINSECTION: case ENDSECTION:
  641.        case BEGINSUBSECTION: case ENDSUBSECTION:
  642.        case BEGINHEADER: case ENDHEADER:
  643.        case BEGINFOOTER: case ENDFOOTER:
  644.        case BEGINTABLE: case ENDTABLE:
  645.         /* no action */
  646.         break;
  647.     }
  648. }
  649.  
  650.  
  651.  
  652. /*
  653.  * ASCII
  654.  */
  655.  
  656. void
  657. ASCII(enum command cmd) {
  658.     int i;
  659.  
  660.     switch (cmd) {
  661.        case CHARRQUOTE:
  662.        case CHARLQUOTE:
  663.         putchar('"');
  664.         break;
  665.        case CHARLSQUOTE:
  666.        case CHARRSQUOTE:
  667.         putchar('\'');
  668.         break;
  669.        case CHARPERIOD:    putchar('.'); break;
  670.        case CHARDASH:    putchar('-'); break;
  671.        case CHARLT:    putchar('<'); break;
  672.        case CHARAMP:        putchar('&'); break;
  673.        case CHARBACKSLASH:    putchar('\\'); break;
  674.        case CHARGT:    putchar('>'); break;
  675.        case CHARDAGGER:    putchar('+'); break;
  676.        case CHARBULLET:    putchar('*'); break;
  677.        case CHARPLUSMINUS: printf("+-"); break;
  678.        case CHANGEBAR:    putchar('|'); break;
  679.  
  680.        case BEGINLINE:
  681.         for (i=0; i<ncnt; i++) putchar('\n');
  682.         break;
  683.        case ENDLINE:
  684.         putchar('\n');
  685.         CurLine++;
  686.         tagc=0;
  687.         break;
  688.  
  689.        case BEGINDOC: case ENDDOC:
  690.        case BEGINBODY: case ENDBODY:
  691.        case BEGINHEADER: case ENDHEADER:
  692.        case BEGINFOOTER: case ENDFOOTER:
  693.        case BEGINSECTION: case ENDSECTION:
  694.        case BEGINSECTHEAD: case ENDSECTHEAD:
  695.        case BEGINSUBSECTHEAD: case ENDSUBSECTHEAD:
  696.        case BEGINBULPAIR: case ENDBULPAIR:
  697.        case BEGINBULLET: case ENDBULLET:
  698.        case BEGINBULTXT: case ENDBULTXT:
  699.        case BEGINSUBSECTION: case ENDSUBSECTION:
  700.  
  701.        case SHORTLINE:
  702.        case BEGINTABLE: case ENDTABLE:
  703.        case BEGINTABLELINE: case ENDTABLELINE:
  704.        case BEGINBOLD: case ENDBOLD:
  705.        case BEGINITALICS: case ENDITALICS:
  706.        case BEGINMANREF: case ENDMANREF:
  707.        case BEGINBOLDITALICS: case ENDBOLDITALICS:
  708.        case BEGINY: case ENDY:
  709.        case BEGINSC: case ENDSC:
  710.         /* nothing */
  711.         break;
  712.     }
  713. }
  714.  
  715.  
  716.  
  717. /*
  718.  * Perl 5 pod ("plain old documentation")
  719.  */
  720.  
  721. void
  722. pod(enum command cmd) {
  723.     static int curindent=0;
  724.     int i;
  725.  
  726.     if (hanging==-1) {
  727.         if (curindent) hanging=curindent; else hanging=5;
  728.     }
  729.  
  730.  
  731.     if (cmd==BEGINBULPAIR) {
  732.         if (curindent && hanging!=curindent) printf("\n=back\n\n");
  733.         if (hanging!=curindent) printf("\n=over %d\n\n",hanging);
  734.         curindent=hanging;
  735.     } else if (cmd==ENDBULPAIR) {
  736.         /* nothing--wait until next command */
  737.     } else if (cmd==BEGINLINE && !scnt) {
  738.         if (curindent) printf("\n=back\n\n");
  739.         curindent=0;
  740.     } else if (cmd==BEGINBODY) {
  741.         if (curindent) {
  742.             printf("\n=back\n\n");
  743.             curindent=0;
  744.             auxindent=0;
  745.         }
  746.     }
  747. /*
  748.        case BEGINBULPAIR:
  749.         printf("=over %d\n\n", hanging);
  750.         break;
  751.        case ENDBULPAIR:
  752.         printf("\n=back\n\n");
  753.         break;
  754. */
  755.     switch (cmd) {
  756.        case BEGINDOC: I=0; break;
  757.  
  758.        case CHARRQUOTE:
  759.        case CHARLQUOTE:
  760.         putchar('"');
  761.         break;
  762.        case CHARLSQUOTE:
  763.        case CHARRSQUOTE:
  764.         putchar('\'');
  765.         break;
  766.        case CHARPERIOD:    putchar('.'); break;
  767.        case CHARDASH:    putchar('-'); break;
  768.        case CHARLT:    putchar('<'); break;
  769.        case CHARAMP:        putchar('&'); break;
  770.        case CHARBACKSLASH:    putchar('\\'); break;
  771.        case CHARGT:    putchar('>'); break;
  772.        case CHARDAGGER:    putchar('+'); break;
  773.        case CHARPLUSMINUS: printf("+-"); break;
  774.        case CHANGEBAR:    putchar('|'); break;
  775.        case CHARBULLET:    putchar('*'); break;
  776.  
  777.        case BEGINLINE:
  778.         for (i=0; i<ncnt; i++) putchar('\n');
  779.         CurLine+=ncnt;
  780.         break;
  781.        case ENDLINE:
  782.         putchar('\n');
  783.         CurLine++;
  784.         tagc=0;
  785.         I=0;
  786.         break;
  787.  
  788.        case BEGINSECTHEAD:    printf("=head1 "); break;
  789.        case BEGINSUBSECTHEAD:    printf("=head2 "); break;
  790.  
  791.        case ENDSECTHEAD:
  792.        case ENDSUBSECTHEAD:
  793.         printf("\n");
  794.         break;
  795.  
  796.        case BEGINBOLD:    printf("B<"); break;
  797.        case BEGINITALICS:    printf("I<"); break;
  798.        case BEGINMANREF:    printf("L<"); break;
  799.  
  800.        case ENDBOLD:
  801.        case ENDITALICS:
  802.        case ENDMANREF:
  803.         printf(">");
  804.         break;
  805.  
  806.        case BEGINBULLET:
  807.         printf("\n=item ");
  808.         break;
  809.        case ENDBULLET:
  810.         printf("\n\n");
  811.         fcharout=0;
  812.         break;
  813.        case BEGINBULTXT:
  814.         fcharout=1;
  815.         auxindent=hanging;
  816.         break;
  817.        case ENDBULTXT:
  818.         auxindent=0;
  819.         break;
  820.  
  821.  
  822.        case ENDDOC:
  823.        case BEGINBODY: case ENDBODY:
  824.        case BEGINHEADER: case ENDHEADER:
  825.        case BEGINFOOTER: case ENDFOOTER:
  826.        case BEGINSECTION: case ENDSECTION:
  827.        case BEGINSUBSECTION: case ENDSUBSECTION:
  828.  
  829.        case SHORTLINE:
  830.        case BEGINTABLE: case ENDTABLE:
  831.        case BEGINTABLELINE: case ENDTABLELINE:
  832.        case BEGINBOLDITALICS: case ENDBOLDITALICS:
  833.        case BEGINY: case ENDY:
  834.        case BEGINSC: case ENDSC:
  835.         /* nothing */
  836.         break;
  837.     }
  838. }
  839.  
  840.  
  841.  
  842. void
  843. Sections(enum command cmd) {
  844.     switch (cmd) {
  845.        case ENDSECTHEAD:
  846.        case ENDSUBSECTHEAD:
  847.         putchar('\n');
  848.        case BEGINDOC:
  849.         fcharout=0;
  850.         break;
  851.        case BEGINSUBSECTHEAD:
  852.         printf("  ");
  853.         /* no break */
  854.        case BEGINSECTHEAD:
  855.         fcharout=1;
  856.         break;
  857.        case CHARRQUOTE:
  858.        case CHARLQUOTE:
  859.         xputchar('"');
  860.         break;
  861.        case CHARLSQUOTE:
  862.        case CHARRSQUOTE:
  863.         xputchar('\'');
  864.         break;
  865.        case BEGINTABLE: case ENDTABLE:
  866.        case BEGINTABLELINE: case ENDTABLELINE:
  867.        case CHARPERIOD:    xputchar('.'); break;
  868.        case CHARDASH:    xputchar('-'); break;
  869.        case CHARBACKSLASH:    xputchar('\\'); break;
  870.        case CHARLT:    xputchar('<'); break;
  871.        case CHARGT:    xputchar('>'); break;
  872.        case CHARAMP:    xputchar('&'); break;
  873.        case CHARDAGGER:    xputchar('+'); break;
  874.        case CHARBULLET:    xputchar('*'); break;
  875.        case CHARPLUSMINUS: xputchar('+'); xputchar('-'); break;
  876.        default:
  877.         /* nothing */
  878.         break;
  879.     }
  880. }
  881.  
  882.  
  883.  
  884. void
  885. Roff(enum command cmd) {
  886.     int i;
  887.  
  888.     switch (cmd) {
  889.        case BEGINDOC:
  890.         I=1;
  891.         printf(".TH %s %s \"generated by RosettaMan\" UCB\n",manName,manSect);
  892.         printf(".\\\"  %s,\n",providence);
  893.         printf(".\\\"  %s\n",anonftp);
  894.         CurLine=1;
  895.         break;
  896.        case BEGINBODY:        /*printf(".LP\n");*/ break;
  897.        case BEGINSECTHEAD:    printf(".SH "); break;
  898.        case BEGINSUBSECTHEAD:printf(".SS "); break;
  899.        case BEGINBULPAIR:    printf(".IP "); break;
  900.        case SHORTLINE:        printf("\n.br"); break;
  901.        case BEGINBOLD:        printf("\\fB"); break;    /* \n.B -- grr! */
  902.        case ENDBOLD:        printf("\\fR"); break;    /* putchar('\n'); */
  903.        case BEGINITALICS:    printf("\\fI"); break;
  904.        case ENDITALICS:        printf("\\fR"); break;
  905.        case BEGINBOLDITALICS:printf("\\f4"); break;
  906.        case ENDBOLDITALICS:    printf("\\fR"); break;
  907.  
  908.        case CHARLQUOTE:        printf("\\*(rq"); break;
  909.        case CHARRQUOTE:        printf("\\*(lq"); break;
  910.        case CHARLSQUOTE:
  911.        case CHARRSQUOTE:
  912.         putchar('\'');
  913.         break;
  914.        case CHARPERIOD:        if (I==1) printf("\\&"); putchar('.'); I++; break;
  915.        case CHARDASH:        printf("\\-"); break;
  916.        case CHARLT:        putchar('<'); break;
  917.        case CHARGT:        putchar('>'); break;
  918.        case CHARAMP:        putchar('&'); break;
  919.        case CHARBULLET:        printf("\\(bu"); break;
  920.        case CHARDAGGER:        printf("\\(dg"); break;
  921.        case CHARPLUSMINUS:    printf("\\(+-"); break;
  922.        case CHANGEBAR:        putchar('|'); break;
  923.        case CHARBACKSLASH:    printf("\\\\"); break;  /* correct? */
  924.  
  925.        case BEGINLINE:
  926.         for (i=0; i<ncnt; i++) putchar('\n');
  927.         break;
  928.  
  929.        case BEGINBULLET:    putchar('"'); break;
  930.        case ENDBULLET:        printf("\"\n"); break;
  931.  
  932.        case ENDLINE:
  933.         tagc=0;
  934.         CurLine++;
  935.         I=1;
  936.         /* no break */
  937.        case ENDSUBSECTHEAD:
  938.        case ENDSECTHEAD:
  939.        case ENDDOC:
  940.         putchar('\n');
  941.         break;
  942.        case ENDBODY:
  943.        case ENDBULPAIR:
  944.        case BEGINBULTXT: case ENDBULTXT:
  945.        case BEGINSECTION: case ENDSECTION:
  946.        case BEGINSUBSECTION: case ENDSUBSECTION:
  947.        case BEGINY: case ENDY:
  948.        case BEGINSC: case ENDSC:
  949.        case BEGINTABLE: case ENDTABLE:
  950.        case BEGINTABLELINE: case ENDTABLELINE:
  951.        case BEGINHEADER: case ENDHEADER:
  952.        case BEGINFOOTER: case ENDFOOTER:
  953.        case BEGINMANREF: case ENDMANREF:
  954.         /* nothing */
  955.         break;
  956.     }
  957. }
  958.  
  959.  
  960.  
  961. /*
  962.  * Ensemble
  963.  */
  964.  
  965. void
  966. EnsembleDumpTags() {
  967.     int i,j,tag;
  968.     int fI=0, fB=0, fH=0;
  969.  
  970.     if (!tagc) return;
  971.  
  972.     printf("}{}{");        /* header */
  973.  
  974.     /* italics */
  975.  
  976.     for (i=0; i<tagc; i++) {
  977.         tag = tags[i].type;
  978.         if (tag==ITALICS||tag==BOLDITALICS) {
  979.             if (!fI) {printf("ITALIC=("); fI=1;}
  980.             printf("(%d,%d,[T])", tags[i].first, tags[i].last);
  981.         }
  982.     }
  983.     if (fI) printf(")");
  984.  
  985.     /* bold */
  986.     for (i=0; i<tagc; i++) {
  987.         tag = tags[i].type;
  988.         if (tag==BOLD||tag==BOLDITALICS) {
  989.             if (!fB) {printf(",BOLD=("); fB=1;}
  990.             printf("(%d,%d,[T])", tags[i].first, tags[i].last);
  991.         }
  992.     }
  993.     if (fB) printf(")");
  994.  
  995.     /* man ref */
  996. /*
  997.     for (i=0; i<tagc; i++) {
  998.         tag = tags[i].type;
  999.         if (tag==MANREF) {
  1000.             if (!fH) {printf(",HYPER=("); fH=1;}
  1001.             printf("(%d,%d,[???])", tags[i].first, tags[i].last);
  1002.         }
  1003.     }
  1004.     if (fH) printf(")");
  1005. */
  1006.  
  1007. /*    printf("}");        /* trailer */
  1008.  
  1009.     tagc=0;
  1010. }
  1011.  
  1012. void
  1013. Ensemble(enum command cmd) {
  1014.  
  1015.     switch (cmd) {
  1016.        case BEGINDOC:
  1017.         I=0;
  1018.         printf("DOCUMENT MANPAGE\n<MANPAGE>\n");
  1019.         escchars = "{}\\";
  1020.         break;
  1021.        case ENDDOC:    printf("</MANPAGE>\n"); break;
  1022.        case BEGINBODY:
  1023.         printf("<SUBSECTIONBODY><BODY>{");
  1024.         break;
  1025.        case ENDBODY:
  1026.         CurLine++;
  1027.         EnsembleDumpTags(); printf("}</BODY></SUBSECTIONBODY>\n");
  1028.         tagc=0;
  1029.         break;
  1030.        case BEGINSECTION:        printf("<SECTION>"); break;
  1031.        case ENDSECTION:            printf("</SECTION>\n"); break;
  1032.        case BEGINSECTHEAD:        printf("<SECTHEAD>{"); break;
  1033.        case ENDSECTHEAD:        tagc=0; I=0; printf("}</SECTHEAD>\n"); break;
  1034.        case BEGINSUBSECTHEAD:    printf("<SUBSECTHEAD>{"); break;
  1035.        case ENDSUBSECTHEAD:        tagc=0; I=0; printf("}</SUBSECTHEAD>\n"); break;
  1036.        case BEGINBULPAIR:        printf("<SUBSECTIONBODY><LISTELEMENT>"); break;
  1037.        case ENDBULPAIR:            printf("</LISTELEMENT></SUBSECTIONBODY>\n"); break;
  1038.        case BEGINBULLET:        printf("<BULLET>{"); break;
  1039.        case ENDBULLET:            tagc=0; I=0; printf("}</BULLET>"); break;
  1040.        case BEGINBULTXT:        printf("<BULLETTEXT>{"); break;
  1041.        case ENDBULTXT:
  1042.         EnsembleDumpTags();
  1043.         CurLine++;
  1044.         printf("}</BULLETTEXT>");
  1045.         break;
  1046.        case BEGINSUBSECTION:    printf("<SUBSECTIONBODY><SUBSECTION>\n"); break;
  1047.        case ENDSUBSECTION:    printf("</SUBSECTION></SUBSECTIONBODY>\n"); break;
  1048.        case SHORTLINE:        /*poppush(prevcmd);*/ break;
  1049.  
  1050.        case CHARRQUOTE:
  1051.        case CHARLQUOTE:
  1052.         putchar('"'); I++;
  1053.         break;
  1054.        case CHARLSQUOTE:
  1055.        case CHARRSQUOTE:
  1056.         putchar('\'');
  1057.         break;
  1058.        case CHARPERIOD:        putchar('.'); I++; break;
  1059.        case CHARDASH:        putchar('-'); I++; break;
  1060.        case CHARBACKSLASH:    putchar('\\'); I++; break;
  1061.        case CHARLT:        putchar('<'); I++; break;
  1062.        case CHARGT:        putchar('>'); I++; break;
  1063.        case CHARAMP:        putchar('&'); I++; break;
  1064.        case CHARBULLET:        printf("\\(bu"); I++; break;
  1065.        case CHARDAGGER:        printf("\\(dg"); I++; break;
  1066.        case CHARPLUSMINUS:    printf("\\(+-"); I++; break;
  1067.  
  1068.        case CHANGEBAR:
  1069.         /* maybe something later */
  1070.        case BEGINLINE: case ENDLINE:
  1071.        case BEGINY: case ENDY:
  1072.        case BEGINHEADER: case ENDHEADER:
  1073.        case BEGINFOOTER: case ENDFOOTER:
  1074.        case BEGINBOLD: case ENDBOLD:
  1075.        case BEGINITALICS: case ENDITALICS:
  1076.        case BEGINBOLDITALICS: case ENDBOLDITALICS:
  1077.        case BEGINSC: case ENDSC:
  1078.        case BEGINTABLE: case ENDTABLE:
  1079.        case BEGINTABLELINE: case ENDTABLELINE:
  1080.  
  1081.        case BEGINMANREF:
  1082.        case ENDMANREF:
  1083.         /* easy strike for hypertext--want to dynamically generate, though */
  1084.  
  1085.         /* nothing */
  1086.         break;
  1087.     }
  1088. }
  1089.  
  1090.  
  1091.  
  1092. /*
  1093.  * SGML
  1094.  */
  1095.  
  1096. /* same as HTML but just has man page-specific DTD */
  1097. /* use Davenport man DTD */
  1098. void
  1099. SGML(enum command cmd) {
  1100.     fprintf(stderr, "SGML format needs a DTD\n");
  1101.     exit(0);
  1102.  
  1103.     /* when get DTD, copy HTML decoding and just change tags */
  1104. }
  1105.  
  1106.  
  1107.  
  1108. /*
  1109.  * HTML
  1110.  */
  1111.  
  1112. void
  1113. HTML(enum command cmd) {
  1114.     static int pre=0;
  1115.     int i;
  1116.     int lasttoc;
  1117.     char *p, *p0;
  1118. /*    static char *bads = "\\<>";*/
  1119.  
  1120.     /* always respond to these signals */
  1121.     switch (cmd) {
  1122.        case CHARLQUOTE:        printf("""); break;
  1123.        case CHARRQUOTE:        printf("""); break;
  1124.        case CHARLSQUOTE:    putchar('`'); break;
  1125.        case CHARRSQUOTE:    putchar('\''); break;
  1126.        case CHARPERIOD:        putchar('.'); break;
  1127.        case CHARDASH:        putchar('-'); break;
  1128.        case CHARBACKSLASH:    putchar('\\'); break;
  1129.        case CHARGT:        printf(">"); break;
  1130.        case CHARLT:        printf("<"); break;
  1131.        case CHARAMP:        printf("&"); break;
  1132.        case CHARBULLET:        putchar(c_bullet); break;
  1133.        case CHARDAGGER:        putchar(c_dagger); break;
  1134.        case CHARPLUSMINUS:    putchar(c_plusminus); break;
  1135.        default: break;
  1136.     }
  1137.  
  1138.     /* while in pre mode... */
  1139.     if (pre) {
  1140.         switch (cmd) {
  1141.            case ENDLINE:    I=0; tagc=0; CurLine++; if (!pmode && scnt) printf("<BR>\n"); break;
  1142.            case ENDTABLE:    printf("</pre><br>\n"); pre=0; fQS=fIQS=pmode=1; break;
  1143.            default:
  1144.             /* nothing */
  1145.             break;
  1146.         }
  1147.         return;
  1148.     }
  1149.  
  1150.     /* usual operation */
  1151.     switch (cmd) {
  1152.        case BEGINDOC:
  1153. /*        escchars = bads;*/
  1154.         printf("<!-- %s, -->\n",providence);
  1155.         printf("<!-- %s -->\n",anonftp);
  1156.         printf("<HTML>\n<HEADER>\n");
  1157. /*        printf("<ISINDEX>\n");*/
  1158.         /* better title possible? */
  1159.         printf("<TITLE>"); printf(manTitle, manName, manSect); printf("</TITLE>\n");
  1160.         printf("</HEADER>\n<BODY>\n");
  1161.         printf("<A HREF=\"#toc\">Table of Contents</A><P>\n");
  1162.         I=0;
  1163.         break;
  1164.        case ENDDOC:
  1165.         /* header and footer wanted? */
  1166.         printf("<P>\n");
  1167.         if (fHeadfoot) {
  1168.             printf("<HR>\n");
  1169.             if (*header) printf("%s\n",header);
  1170.             if (*header2) printf("<BR>%s\n",header2);
  1171.             if (*header3) printf("<BR>%s\n",header3);
  1172.             if (*footer) printf("<BR>%s\n",footer);
  1173.             if (*footer2) printf("<BR>%s\n",footer2);
  1174.         }
  1175.         
  1176.         printf("\n<HR><P>\n");
  1177.         printf("<A NAME=\"toc\"><B>Table of Contents</B></A><P>\n");
  1178.         printf("<UL>\n");
  1179.         for (i=0, lasttoc=BEGINSECTION; i<tocc; lasttoc=toc[i].type, i++) {
  1180.           if (lasttoc!=toc[i].type) {
  1181.             if (toc[i].type==BEGINSUBSECTION) printf("<UL>\n");
  1182.             else printf("</UL>\n");
  1183.           }
  1184.           printf("<LI><A NAME=\"toc%d\" HREF=\"#sect%d\">%s</A></LI>\n", i, i, toc[i].text);
  1185.         }
  1186.         if (lasttoc==BEGINSUBSECTION) printf("</UL>");
  1187.         printf("</UL>\n");
  1188. /*
  1189.         printf(
  1190.           "<HR><I>conversion to HTML by RosettaMan "
  1191.           "available via <A HREF=\"ftp://ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z\">"
  1192.           "anonymous ftp</A></I>\n"
  1193.               );
  1194. */
  1195. /*        printf("<ADDRESS>phelps@cs.berkeley.edu</ADDRESS>\n");*/
  1196.         printf("</BODY></HTML>\n");
  1197.         break;
  1198.        case BEGINBODY:        break;
  1199.        case ENDBODY:        break;
  1200.        case BEGINSECTION:    break;
  1201.        case ENDSECTION:        break;
  1202.        case BEGINSECTHEAD:
  1203.         printf("\n<A NAME=\"sect%d\" HREF=\"#toc%d\"><H2>", tocc, tocc);
  1204.         break;
  1205.        case ENDSECTHEAD:
  1206.         printf("</H2></A>\n");
  1207.         /* useful extraction from files, environment? */
  1208.         break;
  1209.        case BEGINSUBSECTHEAD:
  1210.         printf("\n<A NAME=\"sect%d\" HREF=\"#toc%d\"><H3>", tocc, tocc);
  1211.         break;
  1212.        case ENDSUBSECTHEAD:
  1213.         printf("</H3></A>\n");
  1214.         break;
  1215.        case BEGINSUBSECTION:    break;
  1216.        case ENDSUBSECTION:    break;
  1217.        case BEGINBULPAIR:    printf("<dl>\n"); break;
  1218.        case ENDBULPAIR:        printf("</dl>\n"); break;
  1219.        case BEGINBULLET:    printf("<dt>"); break;
  1220.        case ENDBULLET:        break;
  1221.        case BEGINLINE:        if (ncnt) printf("<P>\n"); break;
  1222.        case ENDLINE:        I=0; tagc=0; CurLine++; if (!pmode && scnt) printf("<BR>\n"); break;
  1223. /*       case ENDLINE:        I=0; tagc=0; putchar('\n'); break;*/
  1224.        case BEGINTABLE:        printf("<br><pre>\n"); pre=1; fQS=fIQS=pmode=0; break;
  1225.        case ENDTABLE:        printf("</pre><br>\n"); pre=0; fQS=fIQS=pmode=1; break;
  1226.        case SHORTLINE:        if (!fIP) printf("<BR>\n"); break;
  1227.        case BEGINBULTXT:    printf("<dd>"); break;
  1228.        case ENDBULTXT:        printf("</dd>\n"); break;
  1229.         /* could use a new list type */
  1230.  
  1231.        case BEGINBOLD:        printf("<B>"); break;
  1232.        case ENDBOLD:        printf("</B>"); break;
  1233.        case BEGINITALICS:    printf("<I>"); break;
  1234.        case ENDITALICS:        printf("</I>"); break;
  1235.        case BEGINBOLDITALICS:printf("<CODE>"); break;
  1236.        case ENDBOLDITALICS:    printf("</CODE>"); break;
  1237.        case BEGINMANREF:
  1238.         for (p=hitxt; *p && *p!='('; p++) /* empty */;
  1239.         *p++='\0'; p0=p;
  1240.         for (; *p && *p!=')'; p++) /* empty */;
  1241.         *p='\0';
  1242.         printf("<A HREF=\""); printf(manRef, hitxt, p0); printf("\">");
  1243.         break;
  1244.        case ENDMANREF:
  1245.         printf("</A>");
  1246.         break;
  1247.  
  1248.        case BEGINSC: case ENDSC:
  1249.        case BEGINY: case ENDY:
  1250.        case BEGINHEADER: case ENDHEADER:
  1251.        case BEGINFOOTER: case ENDFOOTER:
  1252.        case BEGINTABLELINE: case ENDTABLELINE:
  1253.        case CHANGEBAR:
  1254.        default:
  1255.         /* nothing */
  1256.         break;
  1257.     }
  1258. }
  1259.  
  1260.  
  1261.  
  1262. /*
  1263.  * LaTeX
  1264.  */
  1265.  
  1266. void
  1267. LaTeX(enum command cmd) {
  1268.     int i;
  1269.     char *p;
  1270.     static char *bads = "$&%#_{}^"; /* and more to come */
  1271.  
  1272.     switch (cmd) {
  1273.        case BEGINDOC:
  1274.         escchars = bads;
  1275.         printf("%% %s,\n", providence);
  1276.         printf("%% %s\n\n", anonftp);
  1277.         /* definitions */
  1278.         printf(
  1279.           "\\documentstyle{article}\n"
  1280.           "\\def\\thefootnote{\\fnsymbol{footnote}}\n"
  1281.           "\\begin{document}\n"
  1282.               );
  1283.         I=0;
  1284.         break;
  1285.        case ENDDOC:
  1286.         /* header and footer wanted? */
  1287.         printf("\n\\end{document}\n");
  1288.  
  1289.         break;
  1290.        case BEGINBODY:        break;
  1291.        case ENDBODY:        break;
  1292.        case BEGINSECTION:    break;
  1293.        case ENDSECTION:        break;
  1294.        case BEGINSECTHEAD:    printf("\\section{"); tagc=0; break;
  1295.        case ENDSECTHEAD:
  1296.         printf("}");
  1297. /*
  1298.         if (CurLine==1) printf("\\footnote{"
  1299.           "\\it conversion to \\LaTeX\ format by RosettaMan "
  1300.           "available via anonymous ftp from {\\tt ftp.berkeley.edu:/ucb/people/phelps/tcltk}}"
  1301.               );
  1302. */
  1303.         /* useful extraction from files, environment? */
  1304.         printf("\n");
  1305.         break;
  1306.        case BEGINSUBSECTHEAD:printf("\\subsection{"); break;
  1307.        case ENDSUBSECTHEAD:
  1308.         printf("}");
  1309.         break;
  1310.        case BEGINSUBSECTION:    break;
  1311.        case ENDSUBSECTION:    break;
  1312.        case BEGINBULPAIR:    printf("\\begin{itemize}\n"); break;
  1313.        case ENDBULPAIR:        printf("\\end{itemize}\n"); break;
  1314.        case BEGINBULLET:    printf("\\item ["); break;
  1315.        case ENDBULLET:        printf("] "); break;
  1316.        case BEGINLINE:        if (ncnt) printf("\n\n"); break;
  1317.        case ENDLINE:        I=0; tagc=0; /*putchar('\n');*/ CurLine++; break;
  1318.        case BEGINTABLE:        printf("\\begin{verbatim}\n"); break;
  1319.        case ENDTABLE:        printf("\\end{verbatim}\n"); break;
  1320.        case SHORTLINE:        if (!fIP) printf("\n\n"); break;
  1321.        case BEGINBULTXT:    break;
  1322.        case ENDBULTXT:        putchar('\n'); break;
  1323.  
  1324.        case CHARLQUOTE:        printf("``"); break;
  1325.        case CHARRQUOTE:        printf("''"); break;
  1326.        case CHARLSQUOTE:    putchar('`'); break;
  1327.        case CHARRSQUOTE:    putchar('\''); break;
  1328.        case CHARPERIOD:        putchar('.'); break;
  1329.        case CHARDASH:        putchar('-'); break;
  1330.        case CHARBACKSLASH:    printf("$\\backslash$"); break;
  1331.        case CHARGT:        printf("$>$"); break;
  1332.        case CHARLT:        printf("$<$"); break;
  1333.        case CHARAMP:        printf("\\&"); break;
  1334.        case CHARBULLET:        printf("$\\bullet$ "); break;
  1335.        case CHARDAGGER:        printf("\\dag "); break;
  1336.        case CHARPLUSMINUS:    printf("\\pm "); break;
  1337.  
  1338.        case BEGINBOLD:        printf("{\\bf "); break;
  1339.        case BEGINSC:        printf("{\\sc "); break;
  1340.        case BEGINITALICS:    printf("{\\it "); break;
  1341.        case BEGINBOLDITALICS:printf("{\\bf\\it "); break;
  1342.        case BEGINMANREF:    printf("{\\sf "); break;
  1343.        case ENDBOLD:
  1344.        case ENDSC:
  1345.        case ENDITALICS:
  1346.        case ENDBOLDITALICS:
  1347.        case ENDMANREF:
  1348.         putchar('}');
  1349.         break;
  1350.  
  1351.        case BEGINY: case ENDY:
  1352.        case BEGINHEADER: case ENDHEADER:
  1353.        case BEGINFOOTER: case ENDFOOTER:
  1354.        case BEGINTABLELINE: case ENDTABLELINE:
  1355.        case CHANGEBAR:
  1356.         /* nothing */
  1357.         break;
  1358.     }
  1359. }
  1360.  
  1361.  
  1362.  
  1363. /*
  1364.  * Rich Text Format (RTF)
  1365.  */
  1366.  
  1367. /* RTF could use more work */
  1368.  
  1369. void
  1370. RTF(enum command cmd) {
  1371.     int i;
  1372.     char *p;
  1373.     static char *bads = "{}";
  1374.  
  1375.     switch (cmd) {
  1376.        case BEGINDOC:
  1377.         escchars = bads;
  1378.         /* definitions */
  1379.         printf(
  1380.           /* fonts */
  1381.           "{\\rtf1\\deff2 {\\fonttbl"
  1382.           "{\\f20\\froman Times;}{\\f150\\fnil I Times Italic;}"
  1383.           "{\\f151\\fnil B Times Bold;}{\\f152\\fnil BI Times BoldItalic;}"
  1384.           "{\\f22\\fmodern Courier;}{\\f23\\ftech Symbol;}"
  1385.           "{\\f135\\fnil I Courier Oblique;}{\\f136\\fnil B Courier Bold;}{\\f137\\fnil BI Courier BoldOblique;}"
  1386.           "{\\f138\\fnil I Helvetica Oblique;}{\\f139\\fnil B Helvetica Bold;}}"
  1387.           "\n"
  1388.  
  1389.           /* style sheets */
  1390.           "{\\stylesheet{\\li720\\sa120 \\f20 \\sbasedon222\\snext0 Normal;}"
  1391.           "{\\s2\\sb200\\sa120 \\b\\f3\\fs20 \\sbasedon0\\snext2 section head;}"
  1392.           "{\\s3\\li180\\sa120 \\b\\f20 \\sbasedon0\\snext3 subsection head;}"
  1393.           "{\\s4\\fi-1440\\li2160\\sa240\\tx2160 \\f20 \\sbasedon0\\snext4 detailed list;}}"
  1394.           "\n"
  1395.  
  1396. /* more header to come--do undefined values default to nice values? */
  1397.                );
  1398.         I=0;
  1399.         break;
  1400.        case ENDDOC:
  1401.         /* header and footer wanted? */
  1402.         printf("\\par{\\f150 %s,\n%s}", providence, anonftp);
  1403.         printf("}\n");
  1404.         break;
  1405.        case BEGINBODY:        break;
  1406.        case ENDBODY:
  1407.         CurLine++;
  1408.         printf("\\par\n");
  1409.         tagc=0;
  1410.         break;
  1411.        case BEGINSECTION:    break;
  1412.        case ENDSECTION:        printf("\n\\par\n"); break;
  1413.        case BEGINSECTHEAD:    printf("{\\s2 "); tagc=0; break;
  1414.        case ENDSECTHEAD:
  1415.         printf("}\\par");
  1416.         /* useful extraction from files, environment? */
  1417.         printf("\n");
  1418.         break;
  1419.        case BEGINSUBSECTHEAD:printf("{\\s3 "); break;
  1420.        case ENDSUBSECTHEAD:
  1421.         printf("}\\par\n");
  1422.         break;
  1423.        case BEGINSUBSECTION:    break;
  1424.        case ENDSUBSECTION:    break;
  1425.        case BEGINLINE:        /*if (ncnt) printf("\n\n");*/ break;
  1426.        case ENDLINE:        I=0; tagc=0; /*putchar('\n'); CurLine++;*/ break;
  1427.        case SHORTLINE:        if (!fIP) printf("\\line\n"); break;
  1428.        case BEGINBULPAIR:    printf("{\\s4 "); break;
  1429.        case ENDBULPAIR:        printf("}\\par\n"); break;
  1430.        case BEGINBULLET:    break;
  1431.        case ENDBULLET:        printf("\\tab "); fcharout=0; break;
  1432.        case BEGINBULTXT:    fcharout=1; break;
  1433.        case ENDBULTXT:        break;
  1434.  
  1435.        case CHARLQUOTE:        printf("``"); break;
  1436.        case CHARRQUOTE:        printf("''"); break;
  1437.        case CHARLSQUOTE:    putchar('`'); break;
  1438.        case CHARRSQUOTE:    putchar('\''); break;
  1439.        case CHARPERIOD:        putchar('.'); break;
  1440.        case CHARDASH:        putchar('-'); break;
  1441.        case CHARBACKSLASH:    putchar('\\'); break;
  1442.        case CHARGT:        putchar('>'); break;
  1443.        case CHARLT:        putchar('<'); break;
  1444.        case CHARAMP:        putchar('&'); break;
  1445.        case CHARBULLET:        printf("\\bullet "); break;
  1446.        case CHARDAGGER:        printf("\\dag "); break;
  1447.        case CHARPLUSMINUS:    printf("\\pm "); break;
  1448.  
  1449.        case BEGINBOLD:        printf("{\\b "); break;
  1450.        case BEGINSC:        printf("{\\fs20 "); break;
  1451.        case BEGINITALICS:    printf("{\\i "); break;
  1452.        case BEGINBOLDITALICS:printf("{\\b \\i "); break;
  1453.        case BEGINMANREF:    printf("{\\f22 "); break;
  1454.        case ENDBOLD:
  1455.        case ENDSC:
  1456.        case ENDITALICS:
  1457.        case ENDBOLDITALICS:
  1458.        case ENDMANREF:
  1459.         putchar('}');
  1460.         break;
  1461.  
  1462.        case BEGINY: case ENDY:
  1463.        case BEGINHEADER: case ENDHEADER:
  1464.        case BEGINFOOTER: case ENDFOOTER:
  1465.        case BEGINTABLE: case ENDTABLE:
  1466.        case BEGINTABLELINE: case ENDTABLELINE:
  1467.        case CHANGEBAR:
  1468.         /* nothing */
  1469.         break;
  1470.     }
  1471. }
  1472.  
  1473.  
  1474.  
  1475. /*** Kong ***/
  1476. /*
  1477.   I hope the compiler has good common subexpression elimination
  1478.      for all the pointer arithmetic.
  1479. */
  1480.  
  1481. /*
  1482.  level 0: DOC - need match
  1483.  level 1: SECTION - need match
  1484.  level 2: SUBSECTION | BODY | BULLETPAIR
  1485.  level 3: BODY (within SUB) | BULLETPAIR (within SUB) | BULTXT (within BULLETPAIR)
  1486.  level 4: BULTXT (within BULLETPAIR within SUBSECTION)
  1487.  
  1488.  never see: SECTHEAD, SUBSECTHEAD, BULLET
  1489. */
  1490.  
  1491. void
  1492. pop(enum command cmd) {
  1493. /*
  1494.     int i;
  1495.     int p;
  1496.     int match;
  1497.  
  1498.     p=cmdp-1;
  1499.     for (i=cmdp-1;i>=0; i--)
  1500.         if (cmd==cmdstack[i]) { match=i; break; }
  1501. */
  1502.     /* if match, pop off all up to and including match */
  1503.     /* otherwise, pop off one level*/
  1504.  
  1505.  
  1506.     if (Pbt) { (*fn)(ENDBULTXT); Pbt=0; }
  1507.     if (cmd==BEGINBULTXT) return;
  1508.  
  1509.     if (Pb && cmd==BEGINBULPAIR) { (*fn)(ENDBODY); Pb=0; }    /* special */
  1510.     if (Pbp) { (*fn)(ENDBULPAIR); Pbp=0; }
  1511.     if (cmd==BEGINBULPAIR) return;
  1512.  
  1513.     if (Pb) { (*fn)(ENDBODY); Pb=0; }
  1514.     if (cmd==BEGINBODY) return;
  1515.  
  1516.     if (Psub) { (*fn)(ENDSUBSECTION); Psub=0; }
  1517.     if (cmd==BEGINSUBSECTION) return;
  1518.  
  1519.     if (Psect) { (*fn)(ENDSECTION); Psect=0; }
  1520.     if (cmd==BEGINSECTION) return;
  1521. }
  1522.  
  1523. void
  1524. poppush(enum command cmd) {
  1525.     pop(cmd);
  1526.  
  1527.     switch (cmd) {
  1528.        case BEGINBULTXT: Pbt=1; break;
  1529.        case BEGINBULPAIR: Pbp=1; break;
  1530.        case BEGINBODY: Pb=1; break;
  1531.        case BEGINSUBSECTION: Psub=1; break;
  1532.        case BEGINSECTION: Psect=1; break;
  1533.        default:
  1534.         fprintf(stderr, "poppush: unrecognized code %d\n", cmd);
  1535.     }
  1536.  
  1537.     (*fn)(cmd);
  1538.     prevcmd = cmd;
  1539. }
  1540.  
  1541.  
  1542. /* replace gets.  handles hyphenation too */
  1543. char *
  1544. la_gets(char *buf) {
  1545.     static char la_buf[BUFSIZ];    /* can lookahead a full line, but nobody does now */
  1546.     static int fla=0, hy=0;
  1547.     char *ret,*p;
  1548.     int c,i;
  1549.  
  1550.     if (fla) {
  1551.         /* could avoid copying if callers used return value */
  1552.         strcpy(buf,la_buf); fla=0;
  1553.         ret=buf;    /* correct? */
  1554.     } else {
  1555.         /*ret=gets(buf); -- gets is deprecated (since it can read too much?) */
  1556.         /* could do this...
  1557.         ret=fgets(buf, BUFSIZ, stdin);
  1558.         buf[strlen(buf)-1]='\0';
  1559.         ... but don't want to have to rescan line with strlen, so... */
  1560.  
  1561.         i=0; p=buf;
  1562.  
  1563.         /* recover spaces if re-linebreaking */
  1564.         for ( ; hy; hy--, i++) *p++=' ';
  1565.         while ((c=getchar())!=EOF && c!='\n' && i++<BUFSIZ) *p++=c;
  1566.  
  1567.         /* very special case: if in SEE ALSO section, re-linebreak so references aren't linebroken
  1568.            (also do this if fNOHY flag is set) */
  1569.         if (p>buf && p[-1]=='-' && (pmode || fSEEALSO || fNOHY) && isspace(ungetc(getchar(),stdin))) {
  1570.             p--;    /* zap hyphen */
  1571.  
  1572.             /* start getting next line, spaces first ... */
  1573.             while ((c=getchar())!=EOF && isspace(c) && c!='\n') hy++;
  1574.             ungetc(c, stdin);
  1575.  
  1576.             /* ... append next nonspace string to previous ... */
  1577.             while ((c=getchar())!=EOF && !isspace(c) && i++<BUFSIZ) *p++=c;
  1578.             ungetc(c, stdin);
  1579.  
  1580.             /* gobble following spaces (until, perhaps including, end of line) */
  1581.             while ((c=getchar())!=EOF && isspace(c) && c!='\n') /* empty */;
  1582.             if (c=='\n') hy=0; else ungetc(c, stdin);
  1583.         }
  1584.  
  1585.         *p='\0';
  1586.         ret=(c!=EOF)?buf:NULL;
  1587.     }
  1588.  
  1589.     lookahead=ungetc(getchar(), stdin);    /* only looking ahead one character */
  1590.     return ret;    /* change this to line length? */
  1591. }
  1592.  
  1593.  
  1594. /*
  1595.   buf[] == input text (read only)
  1596.   plain[] == output (initial, trailing spaces stripped; tabs=>spaces;
  1597.      underlines, overstrikes => tag array; spaces squeezed, if requested)
  1598.   ccnt = count of changebars
  1599.   scnt = count of initial spaces
  1600.   linelen = length result in plain[]
  1601. */
  1602.  
  1603. int fHead=0;
  1604. int fFoot=0;
  1605.  
  1606. void
  1607. filter() {
  1608.     enum command tagbeginend[][2] = {    /* parallel to enum tagtype */
  1609.         { -1,-1 },
  1610.         { BEGINITALICS, ENDITALICS },
  1611.         { BEGINBOLD, ENDBOLD },
  1612.         { BEGINY, ENDY },
  1613.         { BEGINSC, ENDSC },
  1614.         { BEGINBOLDITALICS, ENDBOLDITALICS },
  1615.         { BEGINMANREF, ENDMANREF }
  1616.     };
  1617.     int curtag;
  1618.     char *p,*q,*r,*bp;
  1619.     char head[BUFSIZ]="";        /* first "word" */
  1620.     char foot[BUFSIZ]="";
  1621.     int header_m=0, footer_m=0;
  1622.     int headlen=0, footlen=0;
  1623.     int line=1-1;
  1624.     int i,j,k,l,off;
  1625.     int sect,subsect,bulpair,osubsect=0;
  1626.     int title=1;
  1627.     int oscnt=-1;
  1628.     int tt=-1;
  1629.     int empty=0,oempty;
  1630.     int fcont=0;
  1631.     int Pnew=0,I0;
  1632.     float s_avg=0.0;
  1633.     int spaceout;
  1634.  
  1635.     if (fMan) indent=-1;
  1636.     I=1;
  1637.     CurLine=1;
  1638.     (*fn)(BEGINDOC); I0=I;
  1639.  
  1640.     /* run through each line */
  1641.     while (la_gets(buf)!=NULL) {
  1642.         line++;
  1643.         if (title) I=I0;
  1644.         filterline(buf,plain);    /* ALL LINES ARE FILTERED */
  1645.         fintable = fTable &&
  1646.             ((!ncnt && fotable) ||
  1647.             (ncnt && bs_cnt>=2 && bs_cnt<=5 && ((float) bs_sum / (float) bs_cnt)>3.0));
  1648.         if (fintable) {
  1649.             if (!fotable) (*fn)(BEGINTABLE);
  1650.         } else if (fotable) {
  1651.             (*fn)(ENDTABLE);
  1652.             I=I0; tagc=0; filterline(buf,plain);    /* rescan first line out of table */
  1653.         }
  1654.  
  1655.         s_avg=(float) s_sum;
  1656.         if (s_cnt>=2) {
  1657.             /* don't count large second space gap */
  1658.             if (scnt2) s_avg= (float) (s_sum - scnt2) / (float) (s_cnt-1);
  1659.             else s_avg= (float) (s_sum) / (float) (s_cnt);
  1660.         }
  1661.  
  1662.         p=plain;    /* points to current character in plain */
  1663.  
  1664.         /*** determine header and global indentation ***/
  1665.         if (fMan && (!fHead || indent==-1)) {
  1666.             if (!linelen) continue;
  1667.             if (*header=='\0') {
  1668.                 /* check for missing first header--but this doesn't catch subsequent pages */
  1669.                 if (strcmp(p,"NAME")==0 || strcmp(p,"Name")==0) {
  1670.                     indent=scnt; /*filterline(buf,plain);*/ scnt=0; I=I0; fHead=1;
  1671.                 } else {
  1672.                     fHead=1;
  1673.                     (*fn)(BEGINHEADER);
  1674.                     /* grab header and its first word */
  1675.                     strcpy(header,p);
  1676. /*                    if ((header_m=linelen-HEADFOOTSKIP)<0) header_m=0;*/
  1677.                     if ((header_m=HEADFOOTSKIP)>linelen) header_m=0;
  1678.                     /*grabphrase(p);*/ strcpy(head,phrase); headlen=phraselen;
  1679.                     la_gets(buf); line++; filterline(buf,plain);
  1680.                     if (linelen) {
  1681.                         strcpy(header2,plain);
  1682.                         lowerline(plain,buf);
  1683.                         if (strncmp(buf,"digital",7)==0 || strncmp(buf,"osf",3)==0) {
  1684.                             fFoot=1;
  1685.                             fSubsections=0;
  1686.                         }
  1687.                     }
  1688.                     (*fn)(ENDHEADER); tagc=0;
  1689.                     continue;
  1690.                 }
  1691.             } else {
  1692.                 /* some idiot pages have a *third* header line, possibly after a null line */
  1693.                 if (*header && scnt>MINMID) { strcpy(header3,p); ncnt=0; /*line++;*/ continue; }
  1694.                 /* indent of first line ("NAME") after header sets global indent */
  1695.                 /* check '<' for Plan 9(?) */
  1696.                 if (*p!='<') {
  1697.                     indent=scnt; I=I0; /*line++;*/ /*filterline(buf,plain);*/scnt=0;
  1698.                 } else continue;
  1699.             }
  1700. /*            if (indent==-1) continue;*/
  1701.         }
  1702.         if (!lindent && scnt) lindent=scnt;
  1703. /*printf("lindent = %d, scnt=%d\n",lindent,scnt);*/
  1704.  
  1705.  
  1706.         /**** for each ordinary line... *****/
  1707.  
  1708.         /*** skip over global indentation */
  1709.         oempty=empty; empty=(linelen==0);
  1710.         if (empty) {ncnt++; continue;}
  1711.  
  1712.         /*** strip out per-page titles ***/
  1713.  
  1714.         if (fMan && (scnt==0 || scnt>MINMID)) {
  1715. /*printf("***ncnt = %d, fFoot = %d, line = %d***", ncnt,fFoot,line);*/
  1716.             if (!fFoot && !isspace(*p) && (scnt>5 || (*p!='-' && *p!='_')) &&
  1717.                 /* don't add ncnt -- line gets absolute line number */
  1718.                 (((ncnt>=2 && line/*+ncnt*/>=61/*was 58*/ && line/*+ncnt*/<70)
  1719.                  || (ncnt>=4 && line/*+ncnt*/>=59 && line/*+ncnt*/<74)
  1720.                  || (ncnt && line/*+ncnt*/>=61 && line/*+ncnt*/<=66))
  1721.                  && (/*lookahead!=' ' ||*/ (s_cnt>=1 && s_avg>1.1) || !falluc) )
  1722.                 ) {
  1723.                 (*fn)(BEGINFOOTER);
  1724.                 /* grab footer and its first word */
  1725.                 strcpy(footer,p);
  1726. /*                if ((footer_m=linelen-HEADFOOTSKIP)<0) footer_m=0;*/
  1727.                 if ((footer_m=HEADFOOTSKIP)>linelen) footer_m=0;
  1728.                 /*grabphrase(p);*/ strcpy(foot,phrase); footlen=phraselen;
  1729.                 footlen--;    /* permit variations at end, as for SGI "Page N" */
  1730.                 la_gets(buf); line++; filterline(buf,plain); if (linelen) strcpy(footer2,plain);
  1731.                 title=1;
  1732.                 (*fn)(ENDFOOTER); tagc=0;
  1733.  
  1734.                 /* if no header on first page, try again after first footer */
  1735.                 if (!fFoot && *header=='\0') fHead=0;    /* this is dangerous */
  1736.                 fFoot=1;
  1737.                 continue;
  1738.             } else
  1739.                 /* a lot of work, but only for a few lines (about 4%) */
  1740.                 if (fFoot && (scnt==0 || scnt+indent>MINMID) &&
  1741.                      (   (headlen && strncmp(head,p,headlen)==0)
  1742.                       || strcmp(header2,p)==0 || strcmp(header3,p)==0
  1743.                       || (footlen && strncmp(foot,p,footlen)==0)
  1744.                       || strcmp(footer2,p)==0
  1745.                       /* try to recognize lines with dates and page numbers */
  1746.                       /* skip into line */
  1747.                       || (header_m && header_m<linelen &&
  1748.                          strncmp(&header[header_m],&p[header_m],HEADFOOTMAX)==0)
  1749.                       || (footer_m && footer_m<linelen &&
  1750.                          strncmp(&footer[footer_m],&p[footer_m],HEADFOOTMAX)==0)
  1751.                       /* skip into line allowing for off-by-one */
  1752.                       || (header_m && header_m<linelen &&
  1753.                          strncmp(&header[header_m],&p[header_m+1],HEADFOOTMAX)==0)
  1754.                       || (footer_m && footer_m<linelen &&
  1755.                          strncmp(&footer[footer_m],&p[footer_m+1],HEADFOOTMAX)==0)
  1756.                       /* or two */
  1757.                       || (header_m && header_m<linelen &&
  1758.                          strncmp(&header[header_m],&p[header_m+2],HEADFOOTMAX)==0)
  1759.                       || (footer_m && footer_m<linelen &&
  1760.                          strncmp(&footer[footer_m],&p[footer_m+2],HEADFOOTMAX)==0)
  1761.                       /* or with reflected odd and even pages */
  1762.                       || (headlen && headlen<linelen &&
  1763.                          strncmp(head,&p[linelen-headlen],headlen)==0)
  1764.                       || (footlen && footlen<linelen &&
  1765.                          strncmp(foot,&p[linelen-footlen],footlen)==0)
  1766.                       )) {
  1767.                 tagc=0; title=1; continue;
  1768.             }
  1769.  
  1770.             /* page numbers at end of line */
  1771.             for(i=0; p[i] && isdigit(p[i]); i++)
  1772.                 /* empty */;
  1773.             if (&p[i]!=plain && !p[i]) {title=1; fFoot=1; continue;}
  1774.         }
  1775.  
  1776.         /*** interline spacing ***/
  1777.         /* multiple \n: paragraph mode=>new paragraph, line mode=>blank lines */
  1778.         /* need to chop up lines for Roff */
  1779.  
  1780.         if (title) ncnt=(scnt!=oscnt || (/*scnt<4 &&*/ isupper(*p)));
  1781.         if (CurLine==1) {ncnt=0; tagc=0;} /* gobble all newlines before first text line */
  1782.         (*fn)(BEGINLINE);
  1783.         if (/*pmode &&*/ ncnt) Pnew=1;
  1784.         title=0; /*ncnt=0;--moved down*/
  1785.         if (fintable) (*fn)(BEGINTABLELINE);
  1786.         oscnt=scnt; fotable=fintable;
  1787.  
  1788.         if (pmode && !Pnew && (prevcmd==BEGINBODY || prevcmd==BEGINBULTXT)) {
  1789.             putchar(' '); I++;
  1790.         }
  1791.  
  1792.         /*** identify structural sections and notify fn */
  1793.  
  1794.         if (fMan) {
  1795.             sect = (scnt==0 && isupper(*p));
  1796.             subsect=(fSubsections && (scnt==2||scnt==3));
  1797. /*            bulpair = (scnt<7 && (*p==c_bullet || *p=='-'));*/
  1798.             /* decode the below */
  1799.             bulpair = ((!auxindent || scnt!=lindent+auxindent) /*!bulpair*/
  1800.                      && ((scnt>=2 && scnt2>5) || scnt>=5 || (tagc>0 && tags[0].first==scnt) ) /* scnt>=2?? */
  1801.                      && (((*p==c_bullet || *p=='-' || *p=='.' || falluc) && (ncnt || scnt2>4)) || 
  1802.                       (scnt2-s_avg>=2 && phrase[phraselen-1]!='.') ||
  1803.                       (scnt2>3 && s_cnt==1)
  1804.                       ));
  1805.             if (bulpair) {
  1806.                 if (tagc>0 && tags[0].first==scnt) {
  1807.                     k=tags[0].last;
  1808.                     for (l=1; l<tagc; l++) {
  1809.                         if (tags[l].first - k <=3)
  1810.                             k=tags[l].last;
  1811.                         else break;
  1812.                     }
  1813.                     phraselen=k-scnt;
  1814.                     for (k=phraselen; plain[k]==' ' && k<linelen; k++) /* nothing */;
  1815.                     if (k>=5 && k<linelen) hanging=k; else hanging=-1;
  1816.                 } else if (scnt2) hanging=phraselen+scnt2;
  1817.                 else hanging=5;
  1818.             } else hanging=0;
  1819.  
  1820. /*            hanging = bulpair? phraselen+scnt2 : 0;*/
  1821. /*if (bulpair) printf("hanging = %d\n",hanging);*/
  1822.             /* maybe, bulpair=0 would be best */
  1823.         }
  1824.  
  1825.         /* certain sections (subsections too?) special, like SEE ALSO */
  1826.         /* to make canonical name as plain, all lowercase */
  1827.         if (sect||subsect) {
  1828.             lowerline(plain,buf);
  1829.             fSEEALSO = (strcmp(buf,"see also")==0 || strcmp(buf,"related information")==0);
  1830.             fFILES = (strcmp(buf,"files")==0);
  1831.         }
  1832.  
  1833.         if (sect) {
  1834.             poppush(BEGINSECTION); (*fn)(BEGINSECTHEAD);
  1835.             addtoc(plain, BEGINSECTION, CurLine);
  1836.         } else if (subsect && !osubsect) {
  1837.             poppush(BEGINSUBSECTION); (*fn)(BEGINSUBSECTHEAD);
  1838.             addtoc(plain, BEGINSUBSECTION, CurLine);
  1839.         } else if (bulpair) {
  1840.             poppush(BEGINBULPAIR); (*fn)(BEGINBULLET);
  1841.             fIP=1; /*grabphrase(plain);*/
  1842.         } else if (Pnew) {
  1843.             poppush(BEGINBODY);
  1844.         }
  1845.         Pnew=0;
  1846.  
  1847.  
  1848.         /* move change bars to left */
  1849.         if (fChangeleft) {
  1850.             if (pmode) (*fn)(CHANGEBAR);
  1851.             else for (i=0; i<ccnt; i++) { xputchar('|'); /* (*fn)(CHANGEBAR); ?*/ }
  1852.         }
  1853.  
  1854.         /* show initial spaces */
  1855.         if (!fIQS && fcharout) {
  1856.             spaceout = (scnt>ccnt)?(scnt-ccnt):0;
  1857.             if (fILQS) { if (spaceout>=lindent) spaceout-=lindent; else spaceout=0; }
  1858.             if (auxindent) { if (spaceout>=auxindent) spaceout-=auxindent; else spaceout=0; }
  1859.             printf("%*s",spaceout,"");
  1860.         }
  1861.  
  1862.  
  1863.         /*** iterate over each character in line, ***/
  1864.         /*** handling underlining, tabbing, copyrights ***/
  1865.  
  1866.         off=(!fIQS&&!pmode)?scnt:0;
  1867.         for (i=0, p=plain, curtag=0, fcont=0; *p; p++,i++,fcont=0) {
  1868.             /* interspersed presentation signals */
  1869.             /* start tags in reverse order of addition (so structural first) */
  1870.             if (curtag<tagc && i+I0+off==tags[curtag].first) {
  1871.                 for (r=hitxt, j=tags[curtag].last-tags[curtag].first, hitxt[j]='\0'; j; j--)
  1872.                     hitxt[j-1]=p[j-1];
  1873.                 (*fn)(tagbeginend[tags[curtag].type][0]);
  1874.             }
  1875.  
  1876.             /* special characters */
  1877.             switch(*p) {
  1878.                case '"':
  1879.                 if (p==plain || isspace(p[-1])) { (*fn)(CHARLQUOTE); fcont=1; }
  1880.                 else if (isspace(p[1])) { (*fn)(CHARRQUOTE); fcont=1; }
  1881.                 break;
  1882.                case '\'':
  1883.                 if (p==plain || isspace(p[-1])) { (*fn)(CHARLSQUOTE); fcont=1; }
  1884.                 else if (isspace(p[1])) { (*fn)(CHARRSQUOTE); fcont=1; }
  1885.                 break;
  1886.                case '-':
  1887.                 /* check for -opt => \-opt */
  1888.                 if (p==plain || (isspace(p[-1]) && !isspace(p[1]))) {
  1889.                   (*fn)(CHARDASH); fcont=1;
  1890.                 }
  1891.                 break;
  1892.                case '\\': (*fn)(CHARBACKSLASH); fcont=1; break;
  1893.                case '<': (*fn)(CHARLT); fcont=1; break;
  1894.                case '>': (*fn)(CHARGT); fcont=1; break;
  1895.                case '&': (*fn)(CHARAMP); fcont=1; break;
  1896.                case c_dagger: (*fn)(CHARDAGGER); fcont=1; break;
  1897.                case c_bullet: (*fn)(CHARBULLET); fcont=1; break;
  1898.                case c_plusminus: (*fn)(CHARPLUSMINUS); fcont=1; break;
  1899.                case '.': (*fn)(CHARPERIOD); fcont=1; break;
  1900.             }
  1901.  
  1902. /*default:*/
  1903.             if (!fcont && fcharout) {
  1904.                 if (strchr(escchars,*p)!=NULL) {putchar('\\');}
  1905.                 putchar(*p); I++;
  1906.             }
  1907.  
  1908.             if (curtag<tagc && i+I0+off+1==tags[curtag].last) {
  1909.                 (*fn)(tagbeginend[tags[curtag].type][1]);
  1910.                 curtag++;
  1911.             }
  1912.  
  1913.             if (fIP && ((*p==' ' && i==phraselen) || *p=='\0')) {
  1914.                 p++;  /* needed but why? */
  1915.                 (*fn)(ENDBULLET); fIP=0;
  1916.                 if (*p!='\0') {
  1917.                     /*oscnt+=phraselen;*/
  1918.                     oscnt+=i;
  1919.                     for (r=p; *r==' '; r++) {
  1920.                         oscnt++;
  1921. /*
  1922.                         i++;
  1923.                         if (fQS || !fcharout) p++;
  1924. */
  1925.                     }
  1926.                 }
  1927.                 p--;    /* increment in loop */
  1928.  
  1929.                 poppush(BEGINBULTXT);
  1930.             }
  1931.         }
  1932.  
  1933.  
  1934.         /*** end of line in buf[] ***/
  1935.         /*** deal with section titles, hyperlinks ***/
  1936.  
  1937.         if (sect) { (*fn)(ENDSECTHEAD); Pnew=1; }
  1938.         else if (subsect) { (*fn)(ENDSUBSECTHEAD); Pnew=1; }
  1939.         else if (fIP) { (*fn)(ENDBULLET); fIP=0; poppush(BEGINBULTXT); }
  1940. /* oscnt not right here */
  1941.         else if (scnt+linelen+spcsqz<MINRM /*&& ncnt*/ && lookahead!='\n'
  1942.                 && prevcmd!=BEGINBULTXT && prevcmd!=ENDSUBSECTHEAD && prevcmd!=ENDSUBSECTHEAD)
  1943.             (*fn)(SHORTLINE);
  1944.         osubsect=subsect;
  1945.  
  1946.         if (fintable) (*fn)(ENDTABLELINE);
  1947.         /*if (!pmode)*/ (*fn)(ENDLINE);
  1948.         ncnt=0;
  1949.         I0=I;    /* save I here in case skipping lines screws it up */
  1950.     }
  1951.  
  1952.     /* wrap up at end */
  1953.     pop(ENDDOC); (*fn)(ENDDOC);
  1954. }
  1955.  
  1956.  
  1957.  
  1958. int
  1959. main(int argc, char *argv[]) {
  1960.     int c;
  1961.     int i;
  1962.     int fname=0;
  1963.     extern char *optarg;
  1964.     extern int optind, opterr;
  1965.     char lcfilter[BUFSIZ];
  1966.  
  1967. #ifdef macintosh
  1968.     extern void InitToolbox();
  1969.     InitToolbox();
  1970. #endif
  1971.  
  1972.     fn=ASCII;        /* default output format */
  1973.  
  1974.     while ((c=getopt(argc,argv,"Kh?f:l:r:bckmTpvn:t:s:y"))!=-1)
  1975.         switch (c) {
  1976.            case 'k': fHeadfoot=1; break;
  1977.            case 'b': fSubsections=1; break;
  1978.            case 'c': fChangeleft=1; break;
  1979.            case 'n': strcpy(manName,optarg); fname=1; break;    /* name & section for when using stdin */
  1980.            case 's': strcpy(manSect,optarg); break;
  1981.            case 'l': manTitle = optarg; break;
  1982.            case 'r': manRef = optarg; break;
  1983.            case 't': TabStops=atoi(optarg); break;
  1984.            case 'm': fMan=0; break;
  1985.            case 'T': fTable=1; break;
  1986.            case 'p': pmode=1-pmode; break;
  1987.            case 'K': fFoot=1; break;
  1988.  
  1989.            case 'f': /* various filters */
  1990.             /* make name lower case */
  1991.             for (i=0; i<strlen(optarg); i++)
  1992.                 lcfilter[i]=tolower(optarg[i]);
  1993.             lcfilter[i]='\0';
  1994.  
  1995.             /* things a bit too irregular for a table of types */
  1996.             if (strncmp(lcfilter,"tkman",UFP)==0) {
  1997.                 fn=TkMan; pmode=fQS=fIQS=0;
  1998.             } else if (strncmp(lcfilter,"ascii",UFP)==0) {
  1999.                 fn=ASCII; pmode=fQS=fIQS=0;
  2000.             } else if (strncmp(lcfilter,"roff",UFP)==0 ||
  2001.                      strncmp(lcfilter,"nroff",UFP)==0 || strncmp(lcfilter,"troff",UFP)==0) {
  2002.                 fn=Roff; pmode=0; fNOHY=1; fChangeleft=1; fIQS=1; fQS=1;
  2003.             } else if (strncmp(lcfilter,"ensemble",UFP)==0) {
  2004.                 fn=Ensemble; pmode=fChangeleft=fQS=fIQS=1;
  2005.             } else if (strncmp(lcfilter,"html",UFP)==0 || strncmp(lcfilter,"www",UFP)==0) {
  2006.                 fn=HTML; pmode=1; fChangeleft=fQS=fIQS=1;
  2007.             } else if (strncmp(lcfilter,"sgml",UFP)==0) {
  2008.                 fprintf(stderr, "Support for the Davenport DTD will be coming Real Soon Now.\n");
  2009.                 exit(0);
  2010.                 /*fn=SGML; pmode=fChangeleft=fQS=fIQS=1;*/
  2011.             } else if (strncmp(lcfilter,"sections",UFP)==0) {
  2012.                 fn=Sections; fQS=fIQS=1;
  2013.             } else if (strncmp(lcfilter,"latex",UFP)==0) {
  2014.                 fn=LaTeX; pmode=fQS=fIQS=1;
  2015.             } else if (strncmp(lcfilter,"rtf",UFP)==0) {
  2016.                 fn=RTF; pmode=fQS=fIQS=1;
  2017.             } else if (strncmp(lcfilter,"pod",UFP)==0) {
  2018.                 fn=pod; pmode=fIQS=fQS=0; fILQS=/*fQS=*/fChangezap=1;
  2019.             } else if (strncmp(lcfilter,"ps",UFP)==0 || strncmp(lcfilter,"postscript",UFP)==0) {
  2020.                 fprintf(stderr, "%s: use groff or psroff to generate PostScript\n", argv[0]);
  2021.                 exit(1);
  2022.             } else if (strncmp(lcfilter,"mif",UFP)==0 || strncmp(lcfilter,"framemaker",UFP)==0) {
  2023.                 fprintf(stderr, "%s: FrameMaker has filters to convert from roff to MIF.\n", argv[0]);
  2024.                 exit(1);
  2025.             } else {
  2026.                 fprintf(stderr, "%s: unknown filter: %s\n", argv[0], optarg);
  2027.                 exit(1);
  2028.             }
  2029.             break;
  2030.            case 'v':
  2031.             printf("RosettaMan v" ROSETTAMANVERSION "\n");
  2032.             exit(0);
  2033.             break;
  2034.            case 'y':
  2035.             fNOHY=1;
  2036.             break;
  2037.            case 'h': case '?':
  2038.             fprintf(stderr,
  2039.                "rman [-f <ASCII|ROFF|TkMan|Ensemble|Sections|HTML|SGML|LaTeX|RTF|pod>]\n"
  2040.                "     [-k(eep head/foot)] [-b(show subsections)] [-c(hangebarstoleft)]\n"
  2041.                "     [-t(abstops) <number>] [-n(ame of man page) <string>] [-s(ection) <number>]\n"
  2042.                "     [-m(an page aggressive parsing off)] [-T(able agressive parsing off)]\n"
  2043.                "     [-v(ersion)] [-K (no page breaks)] [-p(aragraph mode) toggle]\n"
  2044.                "     [-r <man ref printf string>] [-l <title printf string>] [-y (zap hyphens)]\n"
  2045.                "     [<filename>]\n"
  2046.                    );
  2047.             exit(0);
  2048.             break;
  2049.            default:
  2050.             fprintf(stderr, "%s: unidentified option -%c (-h for help)\n",argv[0],c);
  2051.             exit(2);
  2052.         }
  2053.  
  2054.     /* read from given file name(s) */
  2055.     if (optind<argc) {
  2056.         if (!fname) {  /* if no name given, create from file name */
  2057.             strcpy(manName,argv[optind]);
  2058.  
  2059.             /* search backward from end for final dot. split there */
  2060.             for (i=strlen(manName); i>=0; i--) {
  2061.                 if (manName[i]=='.') {
  2062.                     strcpy(manSect,&manName[i+1]);
  2063.                     manName[i]='\0';
  2064.                     break;
  2065.                 }
  2066.             }
  2067.         }
  2068.  
  2069.         if (freopen(argv[optind], "r", stdin)==NULL) {
  2070.             fprintf(stderr, "%s: can't open %s\n", argv[0],argv[optind]);
  2071.             exit(1);
  2072.         }
  2073.     }
  2074.  
  2075.     /* minimal check for roff source: first character dot command or apostrophe comment */
  2076.     lookahead = ungetc(getchar(), stdin);
  2077.     if (lookahead=='.' || lookahead=='\'') {
  2078.         fprintf(stderr, "%s:\tInput looks like [tn]roff source--RosettaMan needs formatted text\n"
  2079.                      "\tfrom `nroff -man' or from */man/cat[1-8oln] directories.\n", argv[0]);
  2080.         exit(1);
  2081.     }
  2082.  
  2083.     filter();
  2084.     return 0;
  2085. }
  2086.